﻿using AiMoWindows.Compiler;
using AiMoWindows.MainActions.Mode;

namespace AiMoWindows.CodeScripts.Init
{
    static class ExampleInitCodes
    {
        static int index = 0;
        public static void GetRandomExample(ScriptComplileMode executerMode, out string[]? mains, out string[]? methods, out string[]? description, out string[]? usings)
        {
            if (executerMode == ScriptComplileMode.GrasshopperScript && CodeLibsForGH.Length > 0)
            {
                mains = CodeLibsForGH[index].Mains;
                methods = CodeLibsForGH[index].Methods;
                description = CodeLibsForGH[index].Description;
                usings = CodeLibsForGH[index].Usings;
                index++;
                if (index >= CodeLibsForGH.Length) index = 0;
            }
            else if (CodeLibs.Length > 0)
            {

                mains = CodeLibs[index].Mains;
                methods = CodeLibs[index].Methods;
                description = CodeLibs[index].Description;
                usings = CodeLibs[index].Usings;
                index++;
                if (index >= CodeLibs.Length) index = 0;
            }
            else
            {
                mains = null;
                methods = null;
                description = null;
                usings = null;
            }
        }

        static CodePair[] GetAllCodePair(string[] examples)
        {

            Random rand = new Random();
            List<KeyValuePair<double, CodePair>> pairs = new List<KeyValuePair<double, CodePair>>();
            foreach (string example in examples)
            {
                pairs.Add(new KeyValuePair<double, CodePair>(rand.NextDouble(), new CodePair(example)));
            }
            return pairs.OrderBy(x => x.Key).Select(x => x.Value).ToArray();
        }

        static readonly CodePair[] CodeLibs = GetAllCodePair(new string[] { example01, example02, example03, example04, example05 });
        static readonly CodePair[] CodeLibsForGH = GetAllCodePair(new string[] { example11 });

        class CodePair
        {
            public string[] Mains { get; }
            public string[] Methods { get; }
            public string[] Description { get; }
            public string[] Usings { get; }
            public CodePair(string contents)
            {
                List<string> mains = new List<string>();
                List<string> funcs = new List<string>() { "" };
                List<string> description = new List<string>();
                List<string> usings = new List<string>();
                PartType type = PartType.None;

                foreach (string line in Utils.Split(Utils.FixNewlines(contents)))
                {
                    if (line.StartsWith("mains:"))
                    {
                        type = PartType.Mains;
                        continue;
                    }
                    else if (line.StartsWith("funcs:"))
                    {
                        type = PartType.Funcs;
                        continue;
                    }
                    else if (line.StartsWith("description:"))
                    {
                        type = PartType.Description;
                        continue;
                    }
                    else if (line.StartsWith("usings:"))
                    {
                        type = PartType.Usings;
                        continue;
                    }

                    switch (type)
                    {
                        case PartType.Mains:
                            mains.Add(line);
                            break;
                        case PartType.Funcs:
                            funcs.Add(line);
                            break;
                        case PartType.Description:
                            description.Add(line);
                            break;
                        case PartType.Usings:
                            usings.Add(line);
                            break;
                    }
                }
                Mains = mains.ToArray();
                Methods = funcs.ToArray();
                Description = description.ToArray();
                Usings = usings.ToArray();
            }
            enum PartType
            {
                Mains,
                Funcs,
                Description,
                Usings,
                None,
            }
        }


        const string example11 =
@"mains:
        this.Component.Message = ""Random Direction Polyline\nFrom Segment Length"";
        //设置输入的Param，输入名字，指定输出类型
        bool re1 = GetItem(""Start Point"", out Point3d lineFrom);
        bool re2 = GetList(""Segments Length"", out double[] segmentLengths);
        if (!re1)
        {
            // 当没有指定Start Point时，默认从零点开始
            lineFrom = Point3d.Origin;
        }
        if (!re2 || segmentLengths.Length < 1)
        {
            // 当没有输入长度时，抛出警告
            this.Component.AddRuntimeMessage(Grasshopper.Kernel.GH_RuntimeMessageLevel.Warning, ""请输入 Segment Length 数据。"");
            segmentLengths = new double[] { 10, 20, 10, 15, 10, 20, 10, 15, 10, 20, 10, 15 };
        }

        List<Vector3d> directions = new List<Vector3d>();

        Random random = new Random();
        Polyline polyline = new Polyline(segmentLengths.Length + 1) { lineFrom };
        Point3d pt = lineFrom;
        foreach (double segLength in segmentLengths) 
        {
            Vector3d vec = new Vector3d(random.NextDouble() - 0.5, random.NextDouble() - 0.5, 0);
            vec.Unitize();
            vec *= segLength;
            pt += vec;
            polyline.Add(pt);
            directions.Add(vec);
        }

        //设置输出的Param，建议使用<T>指定类型。
        SetItem<Polyline>(""Polyline"", polyline);
        SetList<Vector3d>(""Segment Directions"", directions);

        //当然，你也可以使用 GetTree(""Input Lines"",out DataTree<Line> lines); 来处理Tree数据
        //以及写入DataTree数据 SetTree<Line>(""Output Lines"",lines);
funcs:
description:
//案例Grasshopper AiMo代码
";

        const string example01 =
@"mains:
        //如下：使用函数 Print() 显示信息到输出窗口
        Print(""Now is {0}"", DateTime.Now);
    
        //使用函数 Select() 获取Rhino中的物体
        bool re = Select(out RhinoObject obj);
        if(re)
        {
            GeometryBase geo = obj.Geometry.Duplicate();
            BoundingBox bbox = geo.GetBoundingBox(true);
            //使用函数 Show() 在Rhino中预览几何体
            Show(bbox);
            Vector3d moveVec = new Vector3d(0, 0, bbox.Diagonal.Length);

            Show(moveVec, bbox.Max, Color.Red);
            geo.Translate(moveVec);
            Show(geo.GetBoundingBox(true));
            //使用函数 Bake() 添加几何体到Rhino中
            Bake(geo);
        }
        //如下：使用函数 RhinoApp.WriteLine() 输出信息到 Rhino.
        RhinoApp.WriteLine($""My first script is running result = {re}"");
funcs:
description:
//案例Rhino代码
//选择一个几何体，移动并重新添加到Rhino中
";
        const string example02 =
@"mains:
        //如下：使用函数 Print() 显示信息到输出窗口
        Print(""Now is {0}"", DateTime.Now);
        
        //从中心坐标系，根据正负偏移量创建正方形
        //创建正方形半径
        double radius = 1;
        //偏移量的范围
        Interval recRange = new Interval(-radius, radius);
        //输入世界坐标系，Y和X轴范围创建正方形
        Rectangle3d rec = new Rectangle3d(Plane.WorldXY, recRange, recRange);
      
        //（所有的几何体的Move、Scale、Rotate等等位置变换都通过Transform坐标变换矩阵进行坐标变换）
        //缩放的坐标变换
        Transform scale = Transform.Scale(Point3d.Origin, 1.05);
        //旋转的坐标变换
        Transform rotate = Transform.Rotation(0.15, Point3d.Origin);
        //将坐标变换合并，通过乘法进行合并。先变换的放在后面乘。
        Transform mergeTransform = rotate * scale;
      
        //循环100次，建立旋转的方形
        int count = 100;
        for(int i = 0;i < count;i++)
        {
            //将正方形旋转和放大一次。
            rec.Transform(mergeTransform);
          
            //将当前的数量转为0-1的小数
            double percent = (double) i / (double) count;
            //将这个小数改为0-255的整数
            int t = (int) (255.0 * percent * percent);
            //将当前的rec，显示到Rhino中
            Show(rec, Color.FromArgb(t, 255, 255 - t));
            
            //每画一次更新一下显示
            RedrawAll();
        }
funcs:
description:
//案例Rhino代码
//绘制一系列正方形
";
        const string example03 =
@"mains:
        Random random = new Random();
        // 获取所有图层对象
        LayerTable layerTable = RhinoDoc.ActiveDoc.Layers;
        // 遍历图层对象
        for (int i = 0; i < layerTable.Count; i++)
        {
            Layer layer = layerTable[i];
            //原有图层名字
            string rawName = layer.Name;
            // 生成随机名字
            string randomName = ""Layer_"" + random.Next(1000, 9999);
            // 生成随机颜色
            Color randomColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256));
            // 修改图层属性
            layer.Name = randomName;
            layer.Color = randomColor;
            // 应用修改
            layer.CommitChanges();
            Print($""Changed layer {rawName} name to {randomName} with color {randomColor}"");
        }
funcs:
usings:
using Rhino.DocObjects.Tables;
description:
//案例Rhino代码
//将所有图层改为随机名字和随机颜色
";
        const string example04 =
@"mains:
        //设定Box的底面宽和深
        double baseWidth = 6;
        double baseDepth = 6;
    
        //设定Box的高度范围
        double minHeight = 10;
        double maxHeight = 100;
    
        //设定Box的间距
        double spacing = 15;
    
        //设定Box的x轴和y轴的数量
        int xCount = 10;
        int yCount = 10;
    
        //两次循环x方向和y方向上分别建立Box
        for (int i = 0; i <= xCount; i++)
        {
            for (int j = 0; j <= yCount; j++)
            {
                //计算当前位置的Box的高度
                double height = maxHeight + (minHeight - maxHeight) * (Math.Abs(xCount / 2 - i) + Math.Abs(yCount / 2 - j)) / ((yCount + xCount) / 2);
        
                //计算当前Box的位置坐标系Plane
                Point3d origin = new Point3d(i * (spacing + baseWidth), j * (spacing + baseDepth), 0);
                Vector3d normal = Vector3d.ZAxis; //new Vector3d(0, 0, 1);
                Plane boxPlane = new Plane(origin, normal);
        
                //将尺寸信息转化为坐标系的范围（Interval是一个指定最大值和最小值的范围）
                Interval xInterval = new Interval(-baseWidth / 2, baseWidth / 2);
                Interval yInterval = new Interval(-baseDepth / 2, baseDepth / 2);
                Interval zInterval = new Interval(0, height);
        
                //创建Box几何体
                Box box = new Box(boxPlane, xInterval, yInterval, zInterval);
        
                //将几何体添加到Rhino中
                Bake(box);
                //添加之后更新Rhino视窗显示
                RedrawAll();
            }
        }
funcs:
description:
//案例Rhino代码
//在世界坐标原点，生成一个10*10的长方体矩阵，呈现中间高，四边低的趋势。
";
        const string example05 =
@"mains:
        //在命令运行时开启循环
        LoopOn();
funcs:
    //全局变量:高度
    int height = 1;
    //全局变量:步长
    int step = 2;

    //循环函数，将在后台不断循环
    //开启循环：在 Mian 函数中调用 LoopOn();
    //关闭循环：点击 Preview Off 按钮或者调用 LoopOff();
    public override void Loop()
    {
        //当前高度加上步长
        height += step;
        //判断高度在0-255之间，到达限制就反转步长
        if (height > 254 || height < 2) step *= -1;
        if (height < 1) height = 1;
        if (height > 255) height = 255;

        //创建一个高度变化的Box
        BoundingBox box = new BoundingBox(Point3d.Origin, new Point3d(100, 100, height));

        //清除上次Show的内容
        ClearShow();
        //显示本次内容
        Show(Mesh.CreateFromBox(box, 1, 1, 1), Color.FromArgb(255 - height, 255, height));

        //绘制当前Show到Rhino中
        Redraw();
        //停止20毫秒
        Sleep(20);
    }
description:
//案例Rhino代码
//循环Rhino案例，一个高度和显示颜色变化的Box
";
    }
}
